package com.xbongbong.pro.statistic.service.impl;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.SizeConstant;
import com.xbongbong.paas.constant.XbbConstant;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.bi.ChartSourceEnum;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.redis.RedisTimeConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.domain.entity.ChartLimitEntity;
import com.xbongbong.pro.domain.entity.DataSetEntity;
import com.xbongbong.pro.statistic.enums.ChartLimitTypeEnum;
import com.xbongbong.pro.statistic.help.DataSetHelp;
import com.xbongbong.pro.statistic.model.ChartLimitModel;
import com.xbongbong.pro.statistic.pojo.SlaveSourcePojo;
import com.xbongbong.pro.statistic.pojo.dto.BiLimitDTO;
import com.xbongbong.pro.statistic.service.ChartLimitService;
import com.xbongbong.saas.service.toolbox.help.UpdatePackageRedisService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @ClassName ChartLimitServiceImpl
 * @Description 图表限制
 * @Author mengna.Deng
 * @Date 2021/8/23 14:42
 * @Version 1.0
 */
@Service("chartLimitService")
public class ChartLimitServiceImpl implements ChartLimitService {

    private static final Logger LOG = LoggerFactory.getLogger(ChartLimitServiceImpl.class);
    @Resource
    private ChartLimitModel chartLimitModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private UpdatePackageRedisService updatePackageRedisService;
    @Resource
    private DataSetHelp dataSetHelp;

    @Override
    public ChartLimitEntity getByType(Integer type, String corpid) {
        return chartLimitModel.getByType(type,corpid);
    }

    @Override
    public ChartLimitEntity getByAlias(String alias, String corpid) {
        return chartLimitModel.getByAlias(alias, corpid);
    }
    
    /**
     * 切换数据源
     */
    @Override
    public  boolean  switchCK(String corpid, Boolean flag) throws XbbException {
        corpid = "0";
        try {
            saveEntity(corpid, flag,ChartLimitTypeEnum.CHART_DATA_SOURCE_LIMIT,0);
            ChartLimitEntity chartLimit = chartLimitModel.getByType(ChartLimitTypeEnum.CHART_DATA_SOURCE_LIMIT.getCode(), corpid);
            int flagNum = 0;
            if(chartLimit !=null && Objects.equals(chartLimit.getDel(),DelEnum.NORMAL.getDel())){
                flagNum = 1;
            }
            paasRedisHelper.setValue(RedisPrefixConstant.SWITCH_CK_KEY_ENV, corpid, flagNum, RedisConstant.LONG_DURATION);
        } catch (XbbException e) {
            LOG.error("",e);
            return false;
        }
        return true;
    }

    private void saveEntity(String corpid, Boolean flag,ChartLimitTypeEnum chartDataSourceLimit,int limitNum) throws XbbException {
        ChartLimitEntity chartLimitEntity = chartLimitModel.getByType(chartDataSourceLimit.getCode(), corpid);
        if(chartLimitEntity == null){
            //新建
            chartLimitEntity = new ChartLimitEntity();
            chartLimitEntity.setCorpid(corpid);
            chartLimitEntity.setType(chartDataSourceLimit.getCode());
            chartLimitEntity.setAlias(chartDataSourceLimit.getAlias());
            chartLimitEntity.setName(chartDataSourceLimit.getName());
            chartLimitEntity.setLimitNum(limitNum);
            chartLimitEntity.setDel(DelEnum.NORMAL.getDel());
            chartLimitModel.insert(chartLimitEntity);
        }else {
            //更新
            if(flag){
                chartLimitEntity.setLimitNum(limitNum);
                chartLimitEntity.setDel(DelEnum.NORMAL.getDel());
            }else {
                chartLimitEntity.setDel(DelEnum.DELETE.getDel());
            }
            chartLimitModel.updateDel(chartLimitEntity);
        }
    }

    /**
     * 判断此公司是否有特殊限制
     */
    @Override
    public  boolean  getChartLimtFlag(String corpid,String redisKey,ChartLimitTypeEnum chartDataSourceLimit){
        try {
            boolean exists = paasRedisHelper.hasKey(redisKey,chartDataSourceLimit.getAlias());
            if(!exists){
                //key就不存在 预防redis过期
                setElasticCompanyConfig(redisKey,chartDataSourceLimit);
            }
            return paasRedisHelper.isMember(redisKey,chartDataSourceLimit.getAlias(),corpid);
        } catch (Exception e) {
            LOG.error("",e);
            return false;
        }
    }

    @Override
    public boolean setChartLimtFlag(String corpid, Boolean flag,int size) {
        try {
            saveEntity(corpid, flag,ChartLimitTypeEnum.CHART_STATISTICS_TABLE_CK_LIMIT,size);
            setElasticCompanyConfig(RedisPrefixConstant.STATISTICS_TABLE_CK_LIMIT,ChartLimitTypeEnum.CHART_STATISTICS_TABLE_CK_LIMIT);
        } catch (XbbException e) {
            LOG.error("",e);
            return false;
        }
        return true;
    }

    @Override
    public boolean setBiChartLimt(BiLimitDTO biLimitDTO) {
        try {
            ChartLimitTypeEnum chartLimitTypeEnum = ChartLimitTypeEnum.getByCode(biLimitDTO.getCode());
            if (Objects.isNull(chartLimitTypeEnum)) {
                return false;
            }
            saveEntity(biLimitDTO.getCorpid(), true, chartLimitTypeEnum, biLimitDTO.getSize());
            setElasticCompanyConfig(chartLimitTypeEnum.getAlias(), chartLimitTypeEnum);
        } catch (XbbException e) {
            LOG.error("",e);
            return false;
        }
        return true;
    }

    @Override
    public int chartLimitNum(String corpid, int type, int defaultLimit) {
        int limit = defaultLimit;
        ChartLimitEntity chartLimit = getByType(type, corpid);
        if (chartLimit != null) {
            if (chartLimit.getLimitNum() != null && chartLimit.getLimitNum() > 0) {
                limit = chartLimit.getLimitNum();
            }
        }
        return limit;
    }

    /**
     * 根据环境判断此公司是否使用ck（整个钉钉版切过去）
     */
    @Override
    public  boolean  getCkFlag(){
        String corpId = "0";
        String basePackageStr = paasRedisHelper.getValue(RedisPrefixConstant.SWITCH_CK_KEY_ENV, corpId);
        if (!StringUtil.isEmpty(basePackageStr)) {
            if(Objects.equals("1",basePackageStr)){
                return true;
            }
            return false;
        }
        ChartLimitEntity chartLimit = chartLimitModel.getByType(ChartLimitTypeEnum.CHART_DATA_SOURCE_LIMIT.getCode(), corpId);
        boolean flag = false;
        int flagNum = 0;
        if(chartLimit !=null && Objects.equals(chartLimit.getDel(),DelEnum.NORMAL.getDel())){
            flagNum = 1;
            flag = true;
        }
        paasRedisHelper.setValue(RedisPrefixConstant.SWITCH_CK_KEY_ENV, corpId, flagNum, RedisConstant.LONG_DURATION);
        return flag;
    }

    
    /**
     * 防止redis值（全部）
     */
    public void setElasticCompanyConfig(String redisKey,ChartLimitTypeEnum chartDataSourceLimit) {
        List<ChartLimitEntity> companyConfigList = new ArrayList<>();;
        Map<String, Object> param = new HashMap<>();
        param.put("columns", "corpid");
        int type = chartDataSourceLimit.getCode();
        param.put("type", type);
        param.put("del", DelEnum.NORMAL.getDel());
        int pageNum = 10000;
        param.put("pageNum", pageNum);
        boolean flag = true;
        Integer start = 0;
        List companyList;
        while(flag){
            param.put("start",start);
            companyList = chartLimitModel.findEntitys(param);
            if(companyList==null || companyList.size() <= 0){
                flag = false;
            }else{
                companyConfigList.addAll(companyList);
                start =  start + pageNum;
            }
        }

        Set<String> set = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ChartLimitEntity companyConfigEntity : companyConfigList) {
            String corpid = companyConfigEntity.getCorpid();
            set.add(corpid);
        }
        //TODO 一删一加之间可能会有真空期   为了尽量缩短前面都拼装好（有点浪费内存，但触发率低）再做以下两步操作
        //先删除再重建
        paasRedisHelper.removeValue(redisKey, chartDataSourceLimit.getAlias());
        if(CollectionUtils.isNotEmpty(set)){
            paasRedisHelper.addValue4Set(redisKey, chartDataSourceLimit.getAlias(),set, RedisTimeConstant.HUGE_DURATION);
        }
    }

    @Override
    public void chartDataLimit(String corpid, JSONObject driverSource, JSONArray slaveSources,Integer single) throws XbbException {
        //获取数据源数组
        List<SlaveSourcePojo> selectList = getSelectList(corpid, single, driverSource);
        //获取图表数据量限制
        Integer limitNum = getChartDataLimitNum(corpid,selectList,single,slaveSources);
        updatePackageRedisService.chartLimit(selectList, corpid,limitNum);
    }

    @Override
    public Integer getChartDataLimitNum(String corpid,List<SlaveSourcePojo> selectList,Integer single, JSONArray slaveSources) throws XbbException {
        Integer limitNum;
        if (ChartSourceEnum.isDataSet(single)){
            limitNum = Integer.parseInt(String.valueOf(SizeConstant.BI_DATA_SET_LIMIT));
        }else{
            if(CollectionsUtil.isNotEmpty(slaveSources)){
                selectList.addAll(slaveSources.toJavaList(SlaveSourcePojo.class));
                limitNum = commonHelp.getChartLimit(corpid,ChartLimitTypeEnum.BI_MUITI_DATA_NUM_LIMIT.getAlias());
            }else {
                limitNum = commonHelp.getChartLimit(corpid,ChartLimitTypeEnum.BI_DATA_NUM_LIMIT.getAlias());
            }
        }
        return limitNum;
    }

    /**
     * 获取数据源实体
     *
     * @param corpid 公司id
     * @param single 0-多表，1-单表，2-数据集
     * @param driverSource
     * @return java.util.List<com.xbongbong.pro.statistic.pojo.SlaveSourcePojo>
     * @author zyy
     * @date 2022/10/8
     * @version v1.0
     */
    @Override
    public List<SlaveSourcePojo> getSelectList(String corpid,Integer single, JSONObject driverSource) throws XbbException {
        List<SlaveSourcePojo> searchList = new ArrayList<>();
        if(ChartSourceEnum.isDataSet(single)){
            if(driverSource.containsKey(XbbConstant.DATA_SET_ID)){
                //图表渲染时触发校验：driverSource里有dataSetId，无其它关键信息，需要重新到数据库中查
                Long dataSetId = driverSource.getLong(XbbConstant.DATA_SET_ID);
                DataSetEntity dataSetEntity = dataSetHelp.getDataSetEntity(dataSetId, corpid);
                JSONObject board = dataSetEntity.getBoard();
                //不做多余校验，只根据节点type组装数据源实体
                for (String key : board.keySet()) {
                    JSONObject jsonObject = board.getJSONObject(key);
                    String type = jsonObject.getString(XbbConstant.TYPE);
                    if (!Objects.equals(XbbConstant.INPUT,type)){
                        continue;
                    }
                    buildSlaveSource(jsonObject,searchList);
                }
            }else{
                //画板页选取单个数据源时触发校验： 此时前端会处理driverSource字段，直接传递对应的字段值
                buildSlaveSource(driverSource,searchList);
            }
            return searchList;
        }
        //非数据源直接转换driverSource内的数据
        searchList.add(driverSource.toJavaObject(SlaveSourcePojo.class));
        return searchList;
    }

    private void buildSlaveSource(JSONObject jsonObject, List<SlaveSourcePojo> searchList) {
        //目前校验数据量需要用到以下字段
        SlaveSourcePojo slaveSourcePojo = new SlaveSourcePojo();
        slaveSourcePojo.setName(jsonObject.getString(XbbConstant.SOURCE_NAME));
        slaveSourcePojo.setSaasMark(jsonObject.getInteger(XbbConstant.SAAS_MARK));
        slaveSourcePojo.setFormId(jsonObject.getLong(XbbConstant.FORM_ID));
        slaveSourcePojo.setDistributorMark(jsonObject.getInteger(XbbConstant.DISTRIBUTOR_MARK));
        slaveSourcePojo.setBusinessType(jsonObject.getInteger(XbbConstant.BUSINESS_TYPE));
        searchList.add(slaveSourcePojo);
    }
}
